package com.zjl.redis.第13章_缓存双写一致性;

import com.zjl.redis.第13章_缓存双写一致性.一致性类.Cache;
import com.zjl.redis.第13章_缓存双写一致性.一致性类.CacheSQL;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

//import javax.annotation.Resource;


/**
 * 配置文件 application-redisCacheMysql.properties 中
 */
@RestController
public class B_一致性测试 {

    private static final Logger log = LoggerFactory.getLogger(B_一致性测试.class);
    public static final String ORDER_KEY = "ord:";

    @Autowired private CacheSQL cacheSQL;

    @Resource private RedisTemplate redisTemplate;

    @RequestMapping(value = "/add")
    public Integer add(@RequestBody Cache cache){
        System.out.println(cache);
        int o = cacheSQL.insertCache(cache);
        System.out.println("插入了:" + o + "条");
        return o;

    }
    @RequestMapping(value = "/add2")//延迟双删 策略使用
    public Integer add2(@RequestBody Cache cache){
        System.out.println(cache);
        //第一次删除  防止其他人读取脏数据
        redisTemplate.opsForValue().getAndDelete(ORDER_KEY + cache.getId());
        //更新数据库
        int o = cacheSQL.insertCache(cache);

        //延迟删除 数据，防止别的读数据我修改前读取数据后， 写入我修改前的数据，
        // 延迟是保证我的时间范围更长，防止其他线程在我第二次删除后在redis插入旧数据
        CompletableFuture.supplyAsync(()->{
            try { TimeUnit.MILLISECONDS.sleep(100); }catch (Exception e){e.printStackTrace();}
            return redisTemplate.opsForValue().getAndDelete(ORDER_KEY + cache.getId());
        });
        System.out.println("插入了:" + o + "条");
        return o;

    }
    @RequestMapping(value = "/get")
    public Cache get(@RequestBody Cache cache){
        System.out.println(cache.getId());
        Cache c = (Cache) redisTemplate.opsForValue().get(ORDER_KEY + cache.getId());
        System.out.println("redis 里：" + c);
        if(c == null){//redis 无数据
            //大厂用，对于高QPS的优化， 进来就先加锁，保证一个请求操作， 让外面的redis等待一下， 避免击穿mysql
            //双检加锁策略你了解过吗?如何尽量避免缓存击穿?
            synchronized (CacheSQL.class){
                c = cacheSQL.getCache(cache);
                if(c == null){
                    //sql业务数据
                    return c;
                }else{
                    redisTemplate.opsForValue().setIfAbsent(ORDER_KEY + c.getId(),c,7L, TimeUnit.SECONDS);
                }
            }
        }
        return c;
    }
}
